use std::{collections::HashMap, fmt::Display, str::FromStr};
use regex::Regex;

use crate::http::error::HttpParseError;

#[derive(Debug, PartialEq, Eq)]
pub struct HttpUri {
    pub path: Vec<String>,
    pub query: HashMap<String, String>,
}

impl HttpUri {
    pub fn parse_path(path: &str) -> Result<Vec<String>, HttpParseError> {
        // path: "/a/b/c" or just "", result: ["a", "b", "c"]
        if !path.starts_with('/') && !path.is_empty() {
            return Err(HttpParseError::InvalidUri(format!("Invalid path: {}", path)));
        }

        let segments: Vec<String> = path
            .trim_start_matches('/')
            .split('/')
            .filter(|s| !s.is_empty())
            .map(|s| s.to_string())
            .collect();

        Ok(segments)
    }

    pub fn parse_query(query: &str) -> Result<HashMap<String, String>, HttpParseError> {
        let mut map = HashMap::new();

        for pair in query.split('&') {
            if pair.is_empty() {
                continue;
            }

            let mut kv = pair.splitn(2, '=');
            let key = kv.next().unwrap();
            let val = kv.next().unwrap_or("");

            if key.is_empty() {
                return Err(HttpParseError::InvalidUri(format!(
                    "Missing key in query segment '{}'", pair
                )));
            }

            map.insert(key.to_string(), val.to_string());
        }

        Ok(map)
    }
}

impl FromStr for HttpUri {
    type Err = HttpParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // URI: /path/to/endpoint?key1=value1&key2=value2
        let re = Regex::new(r"^(/[^?]*)?(?:\?(.*))?$").unwrap();
        let caps = re
            .captures(s)
            .ok_or_else(|| HttpParseError::InvalidUri(s.to_string()))?;

        let path_str = caps.get(1).map_or("", |m| m.as_str());
        let query_str = caps.get(2).map_or("", |m| m.as_str());

        Ok(HttpUri {
            path: Self::parse_path(path_str)?,
            query: Self::parse_query(query_str)?,
        })
    }
}
impl Display for HttpUri {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "/{}", self.path.join("/"))?;

        if !self.query.is_empty() {
            let query_string = self
                .query
                .iter()
                .map(|(k, v)| format!("{}={}", k, v))
                .collect::<Vec<_>>()
                .join("&");
            write!(f, "?{}", query_string)?;
        }

        Ok(())
    }
}

#[allow(unused)]
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test1() {
        let input = "/api/students?id=123&name=Tom";
        let uri = input.parse::<HttpUri>().unwrap();
    
        assert_eq!(uri.path, vec!["api", "students"]);
        assert_eq!(uri.query["id"], "123");
        assert_eq!(uri.query["name"], "Tom");
    
        println!("{}", uri); // /api/students?id=123&name=Tom
    }
}